home *** CD-ROM | disk | FTP | other *** search
- #import "FortuneView.h"
- #import <math.h>
- #import <ctype.h>
- #import "Thinker.h"
-
- // this code is copyright Darcy Brockbank, 1994
- //
- // You may freely reuse and distribute this code in any way shape or
- // form, provided that this notice stays intact.
- //
- // darcy@hasc.ca, samurai@cs.mcgill.ca
- //
- // If you do improve this thing, send me a copy!
- //
- // - darcy
-
- #define INCLUDED (char)0
- #define EXCLUDED (char)1
-
- #define SHADOW 0
- #define HIGHLIGHT 1
- #define PLAIN 2
-
- #define SPEED "FortuneSpeed"
- #define USECOLOR "FortuneUseColor"
- #define F_FILE "FortuneFile"
- #define BACKING "FortuneBacking"
- #define CLEARAFTER "FortuneClearAfter"
- #define EXFONTS "FortuneExcludedFonts"
-
- #define ROT(c) ((isupper(c))? ('A' + (c - 'A' + 13) % 26) : \
- ((islower(c)) ? ('a' + (c - 'a' + 13) % 26) : c))
-
- @interface ThreeField : TextField
- {
- int drawBacking;
- }
-
- - setDrawBacking:(int)flag;
- - drawSelf:(const NXRect *)r :(int)c;
-
- @end
-
- @implementation ThreeField
-
- - setDrawBacking:(int)flag;
- {
- drawBacking = flag;
- return self;
- }
-
-
- - drawSelf:(const NXRect *)r :(int)c;
- {
- NXColor realColor = [self textColor];
- NXRect hoffset=*r;
- if (strlen([self stringValue])==0){
- NXSetColor(NX_COLORBLACK);
- NXRectFill(r);
- } else if (drawBacking==HIGHLIGHT){
- hoffset.origin.x+=1.0;
- hoffset.origin.y+=1.0;
- [cell setTextColor:NX_COLORWHITE];
- [cell drawInside:&hoffset inView:self];
- [cell setTextColor:realColor];
- [cell drawInside:r inView:self];
- } else {
- [cell drawInside:r inView:self];
- }
- return self;
- }
-
- @end
-
- @interface GrayCell : NXBrowserCell
- {
- BOOL isGray;
- }
- - setGrayed:(BOOL)flag;
- @end
-
- @implementation GrayCell
- - setGrayed:(BOOL)flag;
- {
- isGray = flag;
- return self;
- }
-
- - setTextAttributes:sender;
- {
- [super setTextAttributes:sender];
- if (isGray) [sender setTextGray:NX_DKGRAY];
- return self;
- }
-
- @end
-
- @implementation FortuneView
-
- #define MINX 0.0
- #define MINY 0.0
- #define SIZE 36.0
-
- static const char * const fortunes[] = {
- #define NORMAL 0
- "fortunes",
- #define STARTREK 1
- "startrek",
- #define OFFENSIVE 2
- "fortunes-o",
- #define ZIPPY 3
- "zippy",
- 0
- };
-
-
- - newDatabase:sender;
- {
- int sel = [[sender selectedCell] tag];
- if (sel == OFFENSIVE) {
- if (NXRunAlertPanel("Be sure...", "This option will print fortune entries on the screen "
- "which some people will find offensive (hence the name). If you "
- "are offended by offcolor jokes, or sexually explicit remarks, "
- "or are easily offended at all, then I suggest you not use this option. "
- "The fact that these fortunes are included as part of this screen "
- "saver does not imply that I condone or agree with any of them. "
- "They are included since they were part of the Berkeley 'fortune' package.",
- "Continue","Cancel Selection",0)==NX_ALERTALTERNATE)
- {
- [offensiveMatrix perform:@selector(selectCellWithTag:)
- with:(id)fortuneFile
- afterDelay:1
- cancelPrevious:YES];
- return self;
- }
- }
- fortuneFile = sel;
- [self loadWords:fortunes[fortuneFile]];
- [self perform:@selector(writeFileDefault:)
- with:self
- afterDelay:1000
- cancelPrevious:YES];
- [self lockFocus];
- [self doDrawing];
- [self unlockFocus];
- return self;
- }
-
-
-
- - writeThresholdDefault:sender
- {
- char tmp[128];
- sprintf(tmp,"%u",threshold);
- NXWriteDefault([NXApp appName],SPEED,tmp);
- return self;
- }
- - writeColorDefault:sender
- {
- char tmp[128];
- sprintf(tmp,"%s",(useColor)?"YES":"NO");
- NXWriteDefault([NXApp appName],USECOLOR,tmp);
- return self;
- }
-
- - writeFileDefault:sender
- {
- char tmp[128];
- sprintf(tmp,"%d",fortuneFile);
- NXWriteDefault([NXApp appName],F_FILE,tmp);
- return self;
- }
-
-
- - writeBackingDefault:sender
- {
- char tmp[128];
- sprintf(tmp,"%d",gray);
- NXWriteDefault([NXApp appName],BACKING,tmp);
- return self;
- }
-
-
- - setThreshold:sender
- {
- threshold = [sender intValue];
- [self perform:@selector(writeThresholdDefault:)
- with:self
- afterDelay:1000
- cancelPrevious:YES];
- return self;
- }
-
- - setSpeedSlider:sender;
- {
- speedSlider = sender;
- [sender setIntValue:threshold];
- return self;
- }
-
-
- - loadFonts;
- {
- fontNames = [[FontManager new] availableFonts];
- if (fontNames) {
- for(numFonts = 0;fontNames[numFonts];numFonts++);
- }
- return self;
- }
-
-
- - loadWords:(const char *)database;
- {
- char buf[MAXPATHLEN];
-
- if (fortuneStream) {
- NXCloseMemory(fortuneStream,NX_FREEBUFFER);
- }
- sprintf(buf,"%s/db/%s",[appowner moduleDirectory:"Fortune"],database);
- fortuneStream = NXMapFile(buf,NX_READONLY);
- if (fortuneStream) {
- NXGetMemoryBuffer(fortuneStream,&words,&len,&maxlen);
- } else {
- NXRunAlertPanel("File not found.","I couldn't locate the fortune database "
- "'%s'",NULL,NULL,"OK",buf);
- }
- return self;
- }
-
- - (BOOL)isExcluded:(const char *)fontName
- {
- int i,count = numFonts;
- for(i=0;i<count;i++){
- if (strcmp(fontName,fontNames[i])==0) {
- if (excluded[i]==EXCLUDED) {
- return YES;
- } else {
- break;
- }
- }
- }
- return NO;
- }
-
- - prepFont
- {
- float size = SIZE;
- int fn;
- fn = ((unsigned)random()) % numFonts;
-
- while([self isExcluded:fontNames[fn]]){
- fn++;
- if (!fontNames[fn]) fn=0;
- }
- fontNum = fn;
- font = [Font newFont:fontNames[fn] size:size matrix:NX_FLIPPEDMATRIX];
- return self;
- }
-
- - drawSelf:(const NXRect *)r :(int)c
- {
- if (!r || !c || c==2) return self;
-
- NXSetColor(NX_COLORBLACK);
- NXRectFill(r);
- [self doDrawing];
- return self;
- }
-
- - prepColor;
- {
- if (useColor) {
- blue = ((float)((unsigned)random()))/(float)INT_MAX;
- red = ((float)((unsigned)random()))/(float)INT_MAX;
- green = ((float)((unsigned)random()))/(float)INT_MAX;
- } else {
- blue = red = green = (((unsigned)random())%3) / 3.0 + (1.0/3.0);
- }
- currentColor = NXConvertRGBToColor(red,blue,green);
- return self;
- }
-
-
- - prepPosition;
- {
- xpos = (((unsigned)random()) % (unsigned)((maxCoord.x-MINX))) +MINX;
- ypos = (((unsigned)random()) % (unsigned)((maxCoord.y-MINY))) +MINY;
- return self;
- }
-
- - prepWord;
- {
- unsigned i = ((unsigned)random()) % (maxlen-1);
- int j;
- numWords = 0;
- do {
- i--;
- for(i=(i)?i:i-1;(words[i]!='\n' && i);i--);
- } while(i && words[i-1]!='%');
- for(j=0,i++;i<maxlen && !(words[i]=='\n' && words[i+1]=='%');i++,j++) {
- if (j>=maxWordLen) {
- if (!maxWordLen) maxWordLen = 256;
- currentWord = realloc(currentWord,maxWordLen*2);
- maxWordLen*=2;
- }
- if (fortuneFile == OFFENSIVE) {
- currentWord[j]=ROT(words[i]);
- } else {
- currentWord[j]=words[i];
- }
- if (currentWord[j]==' ' || currentWord[j]=='\t'){
- numWords++;
- }
- }
- currentWord[j]='\0';
- return self;
- }
-
- - prep;
- {
- [self prepFont];
- [self prepColor];
- [self prepPosition];
- [self prepWord];
- return self;
- }
-
- - setGray:sender
- {
- gray = [sender state];
- [fe setDrawBacking:gray];
- [self perform:@selector(writeBackingDefault:)
- with:self
- afterDelay:1000
- cancelPrevious:YES];
- [self lockFocus];
- [self doDrawing];
- [self unlockFocus];
- return self;
- }
-
- - useColor:sender;
- {
- useColor = [sender state];
- [self perform:@selector(writeColorDefault:) with:self afterDelay:1000 cancelPrevious:YES];
- return self;
- }
-
- - setHighlightSwitch:sender
- {
- highlightSwitch = sender;
- [highlightSwitch setState:gray];
- return self;
- }
-
- static void do_size(void)
- {
- #ifdef TEST1
- system("ps -u | grep BackSpace.app | grep -v grep | awk ' { print \"Size: V=\"$5\", R=\"$6 } '");
- #endif
- }
-
- - setOffensiveMatrix:sender;
- {
- offensiveMatrix = sender;
- [offensiveMatrix selectCellWithTag:fortuneFile];
- return self;
- }
-
- - doDrawing;
- {
- static const char * oldCurrent = 0;
- NXRect dr = bounds;
-
- do_size();
- [self prep];
- [window disableFlushWindow];
- [fe setStringValue:""];
- [fe display];
- dr = bounds;
- dr.size.width-=10.0;
- [fe setFrame:&dr];
- [window disableDisplay];
- [fe setTextColor:currentColor];
- while(1){
- int sz = [font pointSize];
- if (sz<=2) {
- /*
- * problem in NeXT's stuff it seems. Too many
- * tabs in the TextFields will make the width
- * impossible to calculate. If this happens,
- * then we just try to do our best.
- */
-
- font = [Font newFont:fontNames[fontNum] size:8 matrix:[font matrix]];
- [fe setFrame:&bounds];
- [fe setFont:font];
- #ifdef TEST2
- printf("%s\n",currentWord);
- #endif
- break;
- }
- #ifdef TESTING
- [fe getFrame:&dr];
- printf("Original font: (%s,%d) \t: {{%.0f,%.0f},{%.0f,%.0f}}\n",
- [font familyName],sz,dr.origin.x,dr.origin.y,dr.size.width,dr.size.height);
- #endif
- [fe setFont:font];
- [fe setStringValueNoCopy:currentWord];
- [fe sizeToFit];
- [fe getFrame:&dr];
- #ifdef TESTING
- printf("Sized to fit ================\t: {{%.0f,%.0f},{%.0f,%.0f}}\n",
- dr.origin.x,dr.origin.y,dr.size.width,dr.size.height);
- #endif
- if (dr.size.width>(bounds.size.width-10.0)) {
- font = [Font newFont:fontNames[fontNum] size:sz-2 matrix:[font matrix]];
- } else if (dr.size.height>=bounds.size.height) {
- font = [Font newFont:fontNames[fontNum] size:sz-2 matrix:[font matrix]];
- } else {
- break;
- }
- }
- [window reenableDisplay];
- [fe moveTo:(bounds.size.width-dr.size.width)/2.0
- :(bounds.size.height-dr.size.height)/2.0];
- [fe display];
- [[window reenableFlushWindow] flushWindow];
- oldCurrent = currentWord;
- return self;
- }
-
- - oneStep
- {
- static unsigned lastTime = 0;
- unsigned thisTime = currentTimeInMs();
-
- if (thisTime-lastTime<(threshold*numWords)) return self;
- lastTime = thisTime;
- if (!appowner) return self;
- [window makeFirstResponder:fe];
- [self doDrawing];
- return self;
- }
-
-
- - (BOOL)useBufferedWindow
- {
- return YES;
- }
-
- - allocTextObject;
- {
- id tf = [[ThreeField alloc] initFrame:&frame];
- [tf allocateGState];
- [tf setClipping:NO];
- [tf setBezeled:NO];
- [tf setBordered:NO];
- // [tf setBackgroundGray:NX_BLACK];
- [tf setBackgroundTransparent:YES];
- [tf setAutodisplay:NO];
- [tf setEditable:NO];
- [tf setSelectable:YES];
- [self addSubview:tf];
- return tf;
- }
-
-
- - setup;
- {
- char buf[MAXPATHLEN];
- appowner = BSThinker();
- sprintf(buf,"%s/FortuneView.nib",[appowner moduleDirectory:"Fortune"]);
- [NXApp loadNibFile:buf owner:self withNames:NO];
- [self loadWords:fortunes[fortuneFile]];
- [speedSlider setIntValue:threshold];
- [highlightSwitch setState:gray];
- fe = [self allocTextObject];
- [fe setDrawBacking:gray];
- [colorSwitch setState:useColor];
- [availableBrowser setCellClass:[GrayCell class]];
- return self;
- }
-
- #define STRDUP(a) ((a)?(strcpy(malloc((strlen(a)+1)*sizeof(char)),a)):0)
-
- - writeExcludedDefault:sender
- {
- int i,length,count = numFonts;
- char * def;
- char * out;
- for(length=i=0;i<count;i++){
- if (excluded[i] == EXCLUDED){
- length += (strlen(fontNames[i])+4);
- }
- }
- def = malloc((length+1) * sizeof(char));
- *def='\0';
- for(out=def,length=i=0;i<count;i++){
- if (excluded[i] == EXCLUDED){
- const char * current = fontNames[i];
- length = strlen(current);
- strcat(out,current);
- if (i<(count-1)) strcat(out,"; ");
- out+=length+2;
- }
- }
- NXWriteDefault([NXApp appName],EXFONTS,def);
- free(def);
- return self;
- }
-
- - showFonts:sender;
- {
- [fontWindow makeKeyAndOrderFront:self];
- return self;
- }
-
- - (int)browser:sender fillMatrix:matrix inColumn:(int)col;
- {
- int i;
- [matrix renewRows:0 cols:0];
- /*
- * I've had some trouble doing this through the browser.
- */
- [[matrix setPrototype:[[GrayCell alloc] init]] free];
- for(i=0; fontNames[i]; i++){
- id cell;
- [matrix addRow];
- cell = [matrix cellAt:i:0];
- [cell setStringValueNoCopy:fontNames[i]];
- [cell setGrayed:[self isExcluded:fontNames[i]]];
- [cell setLoaded:YES];
- [cell setLeaf:YES];
- }
- return i;
- }
-
- - setAvailableBrowser:sender;
- {
- availableBrowser = sender;
- [sender setTarget:self];
- [sender setDoubleAction:@selector(toggleFont:)];
- return self;
- }
-
- - toggleFont:sender;
- {
- id matrix = [availableBrowser matrixInColumn:0];
- id cell = [matrix cellAt:[matrix selectedRow]:0];
- if ([self isExcluded:[cell stringValue]]){
- [self removeFont:self];
- } else {
- [self addFont:self];
- }
- return self;
- }
-
- /*
- * ADD to the excluded list!
- */
- - addFont:sender;
- {
- id matrix = [availableBrowser matrixInColumn:0];
- id cell = [matrix cellAt:[matrix selectedRow]:0];
-
- int i,count;
- for(count=i=0;i<numFonts;i++) count+=(excluded[i]==INCLUDED);
- if (count==1) {
- NXRunAlertPanel("Hey!", "You have to keep at least one font around!",0,0,0);
- } else {
- excluded[[matrix selectedRow]]=EXCLUDED;
- [[availableBrowser window] disableFlushWindow];
- [cell setGrayed:YES];
- [availableBrowser display];
- [[[availableBrowser window] reenableFlushWindow] flushWindow];
- [self perform:@selector(writeExcludedDefault:) with:self afterDelay:1000 cancelPrevious:YES];
- }
- return self;
- }
-
- /*
- * REMOVE from the excluded list!
- */
- - removeFont:sender;
- {
- int col = [availableBrowser selectedColumn];
- if (col==0) {
- id matrix = [availableBrowser matrixInColumn:col];
- int row = [matrix selectedRow];
- if (row>=0){
- excluded[row]=INCLUDED;
- [self perform:@selector(writeExcludedDefault:) with:self afterDelay:1000 cancelPrevious:YES];
- [[availableBrowser window] disableFlushWindow];
- [[matrix cellAt:row:0] setGrayed:NO];
- [availableBrowser display];
- [[[availableBrowser window] reenableFlushWindow] flushWindow];
- }
- }
- return self;
- }
-
- - excludeFont:(const char *)p;
- {
- int i;
- for(i=0;fontNames[i];i++){
- if (strcmp(p,fontNames[i])==0){
- excluded[i]=EXCLUDED;
- break;
- }
- }
- return self;
- }
-
- - setExcludedFonts:(const char *)list;
- {
- char array[strlen(list)+1];
- char * p;
- int i;
-
- excluded = (char *)malloc(sizeof(char)*numFonts);
- for(i=0;i<numFonts;i++) excluded[i]=INCLUDED;
- strcpy(array,list);
- for(i=0,p=array;;i++){
- if (array[i]==';' || array[i]=='\0') {
- int j;
- BOOL shouldBreak = (array[i]=='\0');
- array[i]='\0';
- for(j=i-1;(array[j]=='\t' || array[j]==' ');j--) array[j]='\0';
- [self excludeFont:p];
- if (shouldBreak) break;
- p=array+i+1;
- while(*p==' ' || *p=='\t') p++;
- }
- }
- return self;
- }
-
- - initFrame:(const NXRect *)frameRect
- {
- const char *t;
- [super initFrame:frameRect];
- [self allocateGState]; // For faster lock/unlockFocus
- [self setClipping:NO];// even faster...
-
- [self loadFonts];
- [self newViewSize];
- srandom(time(0));
- if (t = NXReadDefault([NXApp appName],SPEED)){
- threshold = atoi(t);
- } else {
- threshold = 200;
- }
- if (t= NXReadDefault([NXApp appName],BACKING)){
- gray = atoi(t);
- } else {
- gray = PLAIN;
- }
- if (t= NXReadDefault([NXApp appName],USECOLOR)){
- useColor = (strcmp(t,"YES")==0);
- } else {
- useColor = YES;
- }
- if (t= NXReadDefault([NXApp appName],F_FILE)){
- fortuneFile = (atoi(t));
- } else {
- fortuneFile = NORMAL;
- }
- if (t = NXReadDefault([NXApp appName],EXFONTS)){
- [self setExcludedFonts:t];
- } else {
- [self setExcludedFonts:"Symbol ; Lexi"];
- }
- [self setup];
- return self;
- }
-
- - sizeTo:(NXCoord)width :(NXCoord)height
- {
- [super sizeTo:width :height];
- [self newViewSize];
- return self;
- }
-
- - newViewSize
- {
- if (oldSize.width == bounds.size.width &&
- oldSize.height == bounds.size.height)
- {
- return self;
- }else{
- oldSize.width = bounds.size.width;
- oldSize.height = bounds.size.height;
- }
- maxCoord.x = bounds.size.width-150.0;
- maxCoord.y = bounds.size.height;
- if (maxCoord.x < 0) maxCoord.x = 0;
- if (maxCoord.y < 0) maxCoord.y = 0;
- [fe setFrame:&frame];
- if ([self window]){
- [self display];
- }
- return self;
- }
-
- - (const char *)windowTitle
- {
- return "FortuneView";
- }
-
- - inspector:sender
- {
- appowner=sender;
- if (!sharedInspectorPanel){
- [self setup];
- }
- return sharedInspectorPanel;
- }
-
- @end
-
-